/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package logicaNegocio;

import Utiles.Utiles;
import dtos.DTODetalleProducto;
import dtos.DTODetalleServicio;
import dtos.DTOFilaPedidoNegocio;
import dtos.DTOPedido;
import dtos.DTOProducto;
import dtos.DTOServicio;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import logicaNegocio.estrategia.EstrategiaDescuento;
import logicaNegocio.estrategia.FabricaEstrategiaDescuento;
import persistencia.Agente;
import persistencia.Criterio;
import persistencia.FabricaCriterio;
import persistencia.FachadaPersistencia;
import persistencia.agente.Empleado;
import persistencia.agente.EstadoPedido;
import persistencia.agente.FichaInternacion;
import persistencia.agente.HistoricoPrecio;
import persistencia.agente.Pedido;
import persistencia.agente.PedidoAgente;
import persistencia.agente.Producto;
import persistencia.agente.ProductoAgente;
import persistencia.agente.Promocion;
import persistencia.agente.PromocionAgente;
import persistencia.agente.Servicio;

/**
 *
 * @author Sebastian
 */
public class ExpertoRealizarPedido {

    public DTOPedido realizarPedido(DTOPedido pedido, int idControlador) {

        while (true) {
            if (estaTodoDesbloqueado(pedido)) {
                SuscriptorNotificacion.getInstancia().notificar(idControlador, "\nEl pedido nro " + pedido.getNroPedido() + " ingreso al sistema y esta siendo procesado. \n "
                        + "Se enviara uno notificación para que pueda confirmarlo y \n finalizar el proceso");
                bloquearTodoVerificarDisponibilidad(pedido);
                break;
            } else {

                try {
                    Thread.sleep(500);
                    System.out.println("Espera por concurrencia. . .");
                } catch (InterruptedException ex) {
                    Logger.getLogger(ExpertoRealizarPedido.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
        SuscriptorNotificacion.getInstancia().notificar(idControlador, pedido);

        return pedido;
    }

    public boolean validarPaciente(String dni, int nroHabitacion, int nroCama) {
        return true;
    }

    public List<DTOProducto> buscarProductosDisponibles() {
        List<DTOProducto> resultado = null;
        DTOProducto item;
        List<Producto> productos = FachadaPersistencia.getInstancia().buscar(Producto.class, null);

        if (!productos.isEmpty()) {
            resultado = new ArrayList<DTOProducto>();
            for (Producto producto : productos) {
                if (producto.getCantDisponible() > 0) {

                    if (producto.getHistoricosPrecio() != null && !producto.getHistoricosPrecio().isEmpty()) {
                        if (obtenerHistoricoPrecioVigente(producto.getHistoricosPrecio()) != null) {
                            item = new DTOProducto();
                            item.setMarca(producto.getMarca());
                            item.setDescripcion(producto.getDescripcionProducto());
                            item.setNombreProducto(producto.getNombreProducto());
                            item.setSeleccionado(false);
                            item.setCantDisponible(producto.getCantDisponible());
                            item.setCodProducto(producto.getCodProducto());
                            item.setPrecio(obtenerHistoricoPrecioVigente(producto.getHistoricosPrecio()).getPrecio());
                            resultado.add(item);
                        }
                    }

                }
            }
        }

        return resultado;
    }

    public List<DTOServicio> buscarServiciosDisponibles() {
        List<DTOServicio> resultado = null;
        DTOServicio item;
        List<Servicio> servicios = FachadaPersistencia.getInstancia().buscar(Servicio.class, null);

        if (!servicios.isEmpty()) {
            resultado = new ArrayList<DTOServicio>();
            for (Servicio ser : servicios) {
                if (ser.getEmpleadosAsignados() != null && !ser.getEmpleadosAsignados().isEmpty()) {
                    for (Empleado empleado : ser.getEmpleadosAsignados()) {
                        if (empleado.isDisponible()) {

                            if (ser.getHistoricosPrecios() != null && !ser.getHistoricosPrecios().isEmpty()) {
                                if (obtenerHistoricoPrecioVigente(ser.getHistoricosPrecios()) != null) {

                                    item = new DTOServicio();
                                    item.setNombreServicio(ser.getNombreServicio());
                                    item.setDuracion(ser.getTiempoDuracionUnitario());
                                    item.setCodServicio(ser.getCodServicio());

                                    item.setPrecio(obtenerHistoricoPrecioVigente(ser.getHistoricosPrecios()).getPrecio());

                                    resultado.add(item);
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        return resultado;
    }

    public void agregarProductos(DTOPedido dtoPedido, List<DTOProducto> dtoProductos) {
        DTODetalleProducto detalle = null;

        if (dtoPedido.getDtoDetallesProductos() == null) {
            dtoPedido.setDtoDetallesProductos(new ArrayList<DTODetalleProducto>());
        }

        for (DTOProducto dTOProducto : dtoProductos) {
            if (!estaYaCargadoElProducto(dtoPedido.getDtoDetallesProductos(), dTOProducto)) {

                detalle = new DTODetalleProducto();
                detalle.setCantidadSeleccionada(1);
                detalle.setSeleccionado(false);
                detalle.setdTOProducto(dTOProducto);
                detalle.setSubtotal(dTOProducto.getPrecio());

                aplicarPromocion(detalle);
                dtoPedido.getDtoDetallesProductos().add(detalle);
            }
        }
    }

    public void agregarServicios(DTOPedido dtoPedido, List<DTOServicio> dtoServicios) {

        Criterio criterio;
        Servicio servicioSeleccionado;

        DTODetalleServicio detalle;
        if (dtoPedido.getdTODetallesServicios() == null) {
            dtoPedido.setdTODetallesServicios(new ArrayList<DTODetalleServicio>());
        }
        for (DTOServicio dTOServicio : dtoServicios) {
            if (!estaYaCargadoElServicio(dtoPedido.getdTODetallesServicios(), dTOServicio)) {
                detalle = new DTODetalleServicio();

                criterio = FabricaCriterio.getInstancia().obtenerCriterio("codServicio", Criterio.OperadorRelacionalEnum.Igual.getString(), dTOServicio.getCodServicio(), false);
                servicioSeleccionado = FachadaPersistencia.getInstancia().buscar(Servicio.class, criterio).get(0);

                for (Empleado empleado : servicioSeleccionado.getEmpleadosAsignados()) {
                    if (empleado.isDisponible()) {
                        detalle.setLegajoEmpleado(empleado.getLegajo());
                    }
                }

                detalle.setdTOServicio(dTOServicio);
                detalle.setSeleccionado(false);
                detalle.setEmpleadoDisponible(true);
                detalle.setFechaPrestacion(new Date());
                detalle.setSubtotal(dTOServicio.getPrecio());

                dtoPedido.getdTODetallesServicios().add(detalle);
            }
        }
    }

    public EstadoPedido obtenerEstadoPedido(String nombreEstadoPedido) {
        Criterio criterio = FabricaCriterio.getInstancia().obtenerCriterio("nombreEstado", Criterio.OperadorRelacionalEnum.Igual.getString(), nombreEstadoPedido, true);
        List<EstadoPedido> estados = FachadaPersistencia.getInstancia().buscar(EstadoPedido.class, criterio);
        if (!estados.isEmpty()) {
            return estados.get(0);
        }
        return null;
    }

    public static void aplicarPromocion(DTODetalleProducto dTODetalleProducto) {
        List<Promocion> promociones = FachadaPersistencia.getInstancia().buscar(Promocion.class, null);
        List<Promocion> promosAplicadas = null;
        EstrategiaDescuento estrategia = null;
        Criterio criterio = FabricaCriterio.getInstancia().obtenerCriterio("codProducto", Criterio.OperadorRelacionalEnum.Igual.getString(), dTODetalleProducto.getdTOProducto().getCodProducto(), false);
        Producto producto = FachadaPersistencia.getInstancia().buscar(Producto.class, criterio).get(0);

        for (Promocion promocion : promociones) {
            if (Utiles.hoyEstaEntre(promocion.getFechaInicioVigencia(), promocion.getFechaFinVigencia())
                    && (((ProductoAgente) producto).getOid()).equals(((PromocionAgente) promocion).getOidProducto())) {
//                    && dTODetalleProducto.getdTOProducto().getOidProducto().equals(((PromocionAgente) promocion).getOidProducto())) {
                if (promosAplicadas == null) {
                    promosAplicadas = new ArrayList<Promocion>();
                }
                promosAplicadas.add(promocion);
            }
        }
        if (promosAplicadas != null && !promosAplicadas.isEmpty()) {
            for (Promocion promocion : promosAplicadas) {
                estrategia = FabricaEstrategiaDescuento.getInstancia().obtenerEstrategiaDescuento(promocion);
                if (promosAplicadas != null) {
                    estrategia.calcularDescuento(dTODetalleProducto, promocion);
                }
            }
        }
    }

    private boolean estaTodoDesbloqueado(DTOPedido pedido) {
        Producto producto;
        Servicio servicio;
        Criterio criterio;

        for (DTODetalleProducto detalle : pedido.getDtoDetallesProductos()) {
            criterio = FabricaCriterio.getInstancia().obtenerCriterio("codProducto", Criterio.OperadorRelacionalEnum.Igual.getString(), detalle.getdTOProducto().getCodProducto(), false);
            producto = FachadaPersistencia.getInstancia().buscar(Producto.class, criterio).get(0);
            if (producto.isBloqueado()) {
                return false;
            }
        }
        for (DTODetalleServicio detalle : pedido.getdTODetallesServicios()) {
            criterio = FabricaCriterio.getInstancia().obtenerCriterio("codServicio", Criterio.OperadorRelacionalEnum.Igual.getString(), detalle.getdTOServicio().getCodServicio(), false);
            servicio = FachadaPersistencia.getInstancia().buscar(Servicio.class, criterio).get(0);
            for (Empleado empleado : servicio.getEmpleadosAsignados()) {
                if (empleado.isBloqueado()) {
                    return false;
                } else {
                    detalle.setLegajoEmpleado(empleado.getLegajo());
                    return true;
                }
            }
        }
        return true;
    }

    private HistoricoPrecio obtenerHistoricoPrecioVigente(List<HistoricoPrecio> historicos) {
        HistoricoPrecio historico = null;
        for (HistoricoPrecio historicoPrecio : historicos) {
            if (Utiles.hoyEstaEntre(historicoPrecio.getFechaDesde(), historicoPrecio.getFechaHasta())) {
                historico = historicoPrecio;
            }
        }
        return historico;
    }

    private void bloquearTodoVerificarDisponibilidad(DTOPedido pedido) {
        Producto producto;
        Servicio servicio;
        Criterio criterio;
        boolean entrar = true;
        int stock;

        for (DTODetalleProducto detalle : pedido.getDtoDetallesProductos()) {
            criterio = FabricaCriterio.getInstancia().obtenerCriterio("codProducto", Criterio.OperadorRelacionalEnum.Igual.getString(), detalle.getdTOProducto().getCodProducto(), false);
            producto = FachadaPersistencia.getInstancia().buscar(Producto.class, criterio).get(0);

            stock = producto.getCantDisponible() - detalle.getCantidadSeleccionada();
            if (stock >= 0) {
                detalle.setStockSuficiente(true);
            } else {
                detalle.getdTOProducto().setCantDisponible(producto.getCantDisponible());
                detalle.setCantidadSeleccionada(producto.getCantDisponible());
                detalle.setStockSuficiente(false);
                detalle.setSubtotal(producto.getCantDisponible() * obtenerHistoricoPrecioVigente(producto.getHistoricosPrecio()).getPrecio());
                aplicarPromocion(detalle);
            }

            producto.setBloqueado(true);
            FachadaPersistencia.getInstancia().actualizar(Producto.class, producto);
        }

        for (DTODetalleServicio detalle : pedido.getdTODetallesServicios()) {
            criterio = FabricaCriterio.getInstancia().obtenerCriterio("codServicio", Criterio.OperadorRelacionalEnum.Igual.getString(), detalle.getdTOServicio().getCodServicio(), false);
            servicio = FachadaPersistencia.getInstancia().buscar(Servicio.class, criterio).get(0);
            for (Empleado empleadoSer : servicio.getEmpleadosAsignados()) {

                if (!empleadoSer.isBloqueado() && empleadoSer.isDisponible() && entrar) {
                    empleadoSer.setBloqueado(true);
                    detalle.setLegajoEmpleado(empleadoSer.getLegajo());
                    FachadaPersistencia.getInstancia().actualizar(Empleado.class, empleadoSer);
                    entrar = false;
                }
            }
            entrar = true;
        }

    }

    private void restarStockYDesbloquearTodo(DTOPedido pedido) {
        Producto producto;
        Empleado empleado;
        Criterio criterio;
        int stock;

        for (DTODetalleProducto detalleP : pedido.getDtoDetallesProductos()) {
            criterio = FabricaCriterio.getInstancia().obtenerCriterio("codProducto", Criterio.OperadorRelacionalEnum.Igual.getString(), detalleP.getdTOProducto().getCodProducto(), false);
            producto = FachadaPersistencia.getInstancia().buscar(Producto.class, criterio).get(0);
            stock = producto.getCantDisponible() - detalleP.getCantidadSeleccionada();
            producto.setCantDisponible(stock);
            producto.setBloqueado(false);
            FachadaPersistencia.getInstancia().actualizar(Producto.class, producto);
        }

        for (DTODetalleServicio detalleS : pedido.getdTODetallesServicios()) {
            criterio = FabricaCriterio.getInstancia().obtenerCriterio("legajo", Criterio.OperadorRelacionalEnum.Igual.getString(), detalleS.getLegajoEmpleado(), false);
            empleado = FachadaPersistencia.getInstancia().buscar(Empleado.class, criterio).get(0);
            empleado.setDisponible(false);
            empleado.setBloqueado(false);

            FachadaPersistencia.getInstancia().actualizar(Empleado.class, empleado);
        }
    }

    public void confirmarPedido(DTOPedido dTOPedido) {
        DTOFilaPedidoNegocio dTOFilaPedidoNegocio = null;
        Producto producto = null;
        Servicio servicio = null;
        Empleado empleado = null;
        FichaInternacion fichaInternacion = null;
        Criterio criterio;

        Pedido pedido = FabricaEntidad.getInstancia().obtenerEntidad(Pedido.class);

        criterio = FabricaCriterio.getInstancia().obtenerCriterio("nombreEstado", Criterio.OperadorRelacionalEnum.Igual.getString(), "Pendiente", true);
        pedido.setEstadoPedido(FachadaPersistencia.getInstancia().buscar(EstadoPedido.class, criterio).get(0));

        pedido.setNroPedido(dTOPedido.getNroPedido());
        pedido.setFechaIngreso(new Date());

        for (DTODetalleProducto dtoDetalle : dTOPedido.getDtoDetallesProductos()) {
            criterio = FabricaCriterio.getInstancia().obtenerCriterio("codProducto", Criterio.OperadorRelacionalEnum.Igual.getString(), dtoDetalle.getdTOProducto().getCodProducto(), false);
            producto = FachadaPersistencia.getInstancia().buscar(Producto.class, criterio).get(0);
            pedido.agregarDetallePedidoPoducto(dtoDetalle.getdTOProducto().getDescripcion(),
                    dtoDetalle.getCantidadSeleccionada(), dtoDetalle.getSubtotal(), producto);
        }
        for (DTODetalleServicio dtoDetalle : dTOPedido.getdTODetallesServicios()) {
            criterio = FabricaCriterio.getInstancia().obtenerCriterio("codServicio", Criterio.OperadorRelacionalEnum.Igual.getString(), dtoDetalle.getdTOServicio().getCodServicio(), false);
            servicio = FachadaPersistencia.getInstancia().buscar(Servicio.class, criterio).get(0);
            criterio = FabricaCriterio.getInstancia().obtenerCriterio("legajo", Criterio.OperadorRelacionalEnum.Igual.getString(), dtoDetalle.getLegajoEmpleado(), false);
            empleado = FachadaPersistencia.getInstancia().buscar(Empleado.class, criterio).get(0);
            pedido.agregarDetallePedidoServicio(dtoDetalle.getdTOServicio().getDescripcionServicio(),
                    dtoDetalle.getFechaPrestacion(), dtoDetalle.getdTOServicio().getDuracion(),
                    dtoDetalle.getSubtotal(), servicio, empleado);
        }
        criterio = FabricaCriterio.getInstancia().obtenerCriterio("nroFicha", Criterio.OperadorRelacionalEnum.Igual.getString(), dTOPedido.getNroFichaInternacion(), false);
        fichaInternacion = FachadaPersistencia.getInstancia().buscar(FichaInternacion.class, criterio).get(0);
        ((PedidoAgente) pedido).setOidFichaInternacion(((Agente) fichaInternacion).getOid());

        FachadaPersistencia.getInstancia().guardar(Pedido.class, pedido);

        dTOFilaPedidoNegocio = new DTOFilaPedidoNegocio();
        dTOFilaPedidoNegocio.setNroPedido(dTOPedido.getNroPedido());

        dTOFilaPedidoNegocio.setNombreEstadoPedidos(pedido.getEstadoPedido().getNombreEstadoPedido());

        dTOFilaPedidoNegocio.setNroCama(fichaInternacion.getCama().getNroCama());
        dTOFilaPedidoNegocio.seNrotHabitacion(fichaInternacion.getHabitacion().getNroHabitacion());
        dTOFilaPedidoNegocio.setDniPaciente(fichaInternacion.getPaciente().getDni());

        restarStockYDesbloquearTodo(dTOPedido);
        SuscriptorPedidoNuevo.getInstancia().notificar(dTOFilaPedidoNegocio);
    }

    public void desbloquearTodo(DTOPedido pedido) {
        Producto producto;
        Empleado empleado;
        Criterio criterio;

        for (DTODetalleProducto detalle : pedido.getDtoDetallesProductos()) {
            criterio = FabricaCriterio.getInstancia().obtenerCriterio("codProducto", Criterio.OperadorRelacionalEnum.Igual.getString(), detalle.getdTOProducto().getCodProducto(), false);
            producto = FachadaPersistencia.getInstancia().buscar(Producto.class, criterio).get(0);
            producto.setBloqueado(false);
            FachadaPersistencia.getInstancia().actualizar(Producto.class, producto);
        }

        for (DTODetalleServicio detalle : pedido.getdTODetallesServicios()) {
            criterio = FabricaCriterio.getInstancia().obtenerCriterio("legajo", Criterio.OperadorRelacionalEnum.Igual.getString(), detalle.getLegajoEmpleado(), false);
            empleado = FachadaPersistencia.getInstancia().buscar(Empleado.class, criterio).get(0);
            empleado.setBloqueado(false);
            FachadaPersistencia.getInstancia().actualizar(Empleado.class, empleado);

        }
        
        //agregue esta linea xq si en la confirmación se eliminan productos o servicios. Los eliminados no se desbloqueaban
        desbloquearProductosServicios();
    }

    private boolean estaYaCargadoElProducto(List<DTODetalleProducto> productosCargados, DTOProducto nuevoProducto) {
        if (productosCargados != null) {
            for (DTODetalleProducto dTODetalleProducto : productosCargados) {
                if (dTODetalleProducto.getdTOProducto().getCodProducto() == nuevoProducto.getCodProducto()) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean estaYaCargadoElServicio(List<DTODetalleServicio> serviciosCargados, DTOServicio nuevoServicio) {
        if (serviciosCargados != null) {
            for (DTODetalleServicio dTODetalleSer : serviciosCargados) {
                if (dTODetalleSer.getdTOServicio().getCodServicio() == nuevoServicio.getCodServicio()) {
                    return true;
                }
            }
        }
        return false;
    }
    
    private void desbloquearProductosServicios(){
        List<Empleado> empleados = FachadaPersistencia.getInstancia().buscar(Empleado.class, null);
        List<Producto> productos = FachadaPersistencia.getInstancia().buscar(Producto.class, null);
        
        if (empleados != null) {
            for (Empleado emp : empleados) {
                emp.setBloqueado(false);
                FachadaPersistencia.getInstancia().actualizar(Empleado.class, emp);
            }
        }
        if(productos != null){
            for (Producto producto : productos) {
                producto.setBloqueado(false);
                FachadaPersistencia.getInstancia().actualizar(Producto.class, producto);
            }
        }
        
    }
}
